//
// fields.h
// libprotea
// Tools Library for the Protea Project / Fields definitions
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

//
//  Fields Definitions
//
#ifndef __PROTEA_FIELDS_H__
#define __PROTEA_FIELDS_H__

/**
	\file fields.h
	\brief Field class definition
*/

#include "../Appdic/appdic.h"

// Forward class definitions
class XCBuffer;
class XCAppInterface;
class XCTransport;

struct date_conv_type {
	int day, month, year;
	int hours, minutes, seconds;
};

/// Contains all functionality for a field
class __EXPORT XCField : public XCBase {
public:
	friend class XCBuffer;

	/**
		\brief Constructor
	*/
	XCField();

	virtual ~XCField();

	/**
		\brief Copy constructor
	*/
	XCField( const XCField& new_field );

	/**
		\brief Creates an exact copy of the field, including data
	*/
	XCField* Clone() const;


	// Operators
	bool operator==(const XCField&) const;
	bool operator==(const long) const;

	bool operator!=(const XCField&) const;
	bool operator!=(const long) const;

	bool operator<(const XCField&) const;
	bool operator<(const long) const;

	bool operator<=(const XCField&) const;
	bool operator<=(const long) const;

	bool operator>(const XCField&) const;
	bool operator>(const long) const;

	bool operator>=(const XCField&) const;
	bool operator>=(const long) const;


	//
	// Basic field information
	//

	/**
		\brief Get Description Message number
	*/
	int		GetDescrMessage() const;

	/**
		\brief Get Field Type
	*/
	int		GetType() const;

	/**
		\brief Get Field Offset

		Returns a pointer to the actual field data.  Should normally use the
		GetXXX and SetXXX functions instead.
	*/
	char *	GetOffset() const;
	
	/**
		\brief Get field flags
	*/
	short int GetFlags() const;

	/**
		\brief Set field flags
	*/
	void	SetFlags( short int new_flags );

	/**
		\brief Get Field Length (usable)
	*/
	long	GetLength()	const;

	/**
		\brief Get Field Length (actual)
	*/
	long	GetAllocSize()	const;

	/**
		\brief Get Field Decimal Places
	*/
	long	GetDecPlaces() const;

	/**
		\brief Get Field Number
	*/
	long	GetFieldNumber() const;

	/**
		\brief Get Field Name
	*/
	char *  GetFieldName() const;

	/**
		\brief Returns the buffer that this field is owned by
	*/
	XCBuffer* GetBuffer() const;

	/**
		\brief Returns true if the field data has been truncated
	*/
	bool GetTruncationFlag() const;

	/**
		\brief Move field data to another field
		\param new_field Field data to move
		\param MoveBlanks If true, will overwrite value with blanks if the source field is blank
	*/
	void MoveField(const XCField* new_field, bool MoveBlanks=false);

	/**
		\brief Returns true if field is considered blank

		Only works for string data-types.  Other types do not have a defined 'blank' state
	*/
	bool IsBlank() const;

	/**
		\brief Returns the state of the changed flag
	*/
	bool IsChanged() const;

	/**
		\brief Marks the field as changed/unchanged
	*/
	void SetChange( const bool new_change );

	/**
		\brief Returns if the field has a "true" value

		Only valid for boolean types
	*/
	bool IsTrue() const;

	/**
		\brief Force string to uppercase
	*/
	void Uppercase();

	/**
		\brief Force string to lowercase
	*/
	void Lowercase();

	//
	// Mathematical operations
	//

	/**
		\brief Sets the current field value to the absolute value
	*/
	void Abs();

	/**
		\brief Adds the value in a given field to the current field
		\param value Field value to add
	*/
	void Add( const XCField* value );

	/**
		\brief Adds the two supplied fields together and places the result in current field
		\param parm1 First field
		\param parm2 Second field
	*/
	void Add( const XCField* parm1, const XCField* parm2 );

	/**
		\brief Adds the given constant value to the current field
		\param value Constant value to add
	*/
	void AddConst( const int value );

	/**
		\brief Subtracts the value in a given field to the current field
		\param value Field value to subtract
	*/
	void Sub( const XCField* value );

	/**
		\brief Subtracts two fields and places the result in the current field
		\param parm1 Field value to subtract from
		\param parm2 Field value to subtract
	*/
	void Sub( const XCField* parm1, const XCField* parm2 );

	/**
		\brief Subtracts the given constant value from the current field
		\param value Constant value to subtract
	*/
	void SubConst( const int value );

	/**
		\brief Multiples the value in a given field to the current field
		\param value Field value to multiply by
	*/
	void Mult( const XCField* value );

	/**
		\brief Multiplies two fields and places the result in the current field
		\param parm1 Field value to multiply
		\param parm2 Field value to multiply
	*/
	void Mult( const XCField* parm1, const XCField* parm2 );

	/**
		\brief Multiplies the given constant value to the current field
		\param value Constant value to multiply by
	*/
	void MultConst( const int value );

	/**
		\brief Divides the value in a given field to the current field
		\param value Field value to divide by
	*/
	void Div( const XCField* value );

	/**
		\brief Divides two fields and places the result in the current field
		\param parm1 Field value to divide from
		\param parm2 Field value to divide
	*/
	void Div( const XCField* parm1, const XCField* parm2 );

	/**
		\brief Divides the current field by the given constant value
		\param value Constant value to divide by
	*/
	void DivConst( const int value );


	// Time/Date addition

	/**
		\brief Add days to the current date
		\param source Source field
	*/
	void AddDays( const XCField* source );
	void AddConstDays( long value );

	/**
		\brief Subtract days from the current date
		\param source Source field
	*/
	void SubDays( const XCField* source );

	/**
		\brief Add months to the current date

		Will add 31 days for each month
	*/
	void AddMonths( const XCField* source );
	void AddConstMonths( long value );

	/**
		\brief Subtract months from the current date

		Will subtract 31 days for each month
	*/
	void SubMonths( const XCField* source );

	/**
		\brief Add years to the current date
	*/
	void AddYears( const XCField* source );
	void AddConstYears( long value );

	/**
		\brief Subtract years from the current date
	*/
	void SubYears( const XCField* source );

	//
	// Comparison routines
	//

	
	/**
		\brief Compares the value in a field to another field
		\param comp_value Field to test against

		Return:
			-1 Field is less than comp_value
			0  Field is equal to comp_value
			1 Field is greater than comp_value
	*/
	int CompareField( const XCField* comp_value ) const;

	/**
		\brief Compares a field to a constant
		\param test_value Value to test against

		Return:
			-1 Field is less than constant
			0  Field is equal to constant
			1 Field is greater than constant
	*/
	int Compare( const long test_value ) const;

	/**
		\brief Compares a field to a constant decimal value
		\param test_value Value to test against

		Return:
			-1 Field is less than constant
			0  Field is equal to constant
			1 Field is greater than constant
	*/
	int CompareDecimal( const double test_value ) const;

	/**
		\brief Compares a field value to zero

		Return:
			-1 Field is less than constant
			0  Field is equal to constant
			1 Field is greater than constant
	*/
	int CompareZero() const;

	/**
		\brief Compares a field to a literal
		\param literal_num Literal to test against

		Return: True/False
	*/
	bool CompareLiteral( const long literal_num ) const;
	

	/**
		\brief Verify that a numeric field is within a specified range
		\param minval Minimum value
		\param maxval Maximum value
	*/
	bool CheckInRange( const long minval, const long maxval ) const;


	//
	// Data setting/getting routines
	//

	/**
		\brief Set field to double value
	*/
	void SetDouble( const double pVal );

	/**
		\brief Set field to float value
	*/
	void SetFloat( const float pVal );

	/**
		\brief Set field to long value
	*/
	void SetLong( const int value );

	/**
		\brief Set field to literal value
	*/
	void SetLiteral( const long value );

	/**
		\brief Set field to short value
	*/
	void SetShort( const short int value );

	/**
		\brief Set field to string value
	*/
	void SetString( const char* newvalue );

	/**
		\brief Set field to boolean value
	*/
	void SetBool( const bool value );

	/**
		\brief Allocate a new memory section for a BLOB and copies data from array into it (deallocates old BLOB if necessary)
	*/
	void SetBLOB( const long size, long int *data );

	/**
		\brief Allocate a new memory section for a BLOB (deallocates old BLOB if necessary)
	*/
	void InitBLOB( const long size );

	/**
		\brief Clear out field
	*/
	int Clear();

	/**
		\brief Fills the fields with the current time/date

		Field must be able to accept date/time data
	*/
	void SetCurrentDate();

	/**
		\brief Set field to it's default value
	*/
	void SetDefault();

	/**
		\brief Returns a double value
		\param coerce Coerce flag

		If the coerce flag is true, will attempt to return the field data as a double, if
		possible, otherwise, it will generate an error if the data type is not double
	*/
	double		GetDouble( const bool coerce = false ) const;

	/**
		\brief Returns a float value
	*/
	float		GetFloat() const;

	/**
		\brief Returns a string value
		\param result Result buffer

		Returns the field data into the user-supplied buffer.  Ensure that the buffer has
		enough space allocated to hold the field data.
	*/
	char*		GetString( char* result ) const;

	/**
		\brief Returns a string value

		Only works on string types
	*/
	const char* GetStringPtr() const;

	/**
		\brief Returns a long value
		\param coerce Coerce flag

		If the coerce flag is true, will attempt to return the field data as a long, if
		possible, otherwise, it will generate an error if the data type is not double
	*/
	int		GetLong( const bool coerce = false ) const;

	/**
		\brief Returns a short value
	*/
	short int	GetShort() const;

	/**
		\brief Returns a bool value
	*/
	bool		GetBool() const;

	/**
		\brief Return a BLOB size (if !validblob returns 0 unless GetAnyway = true)
	*/
	long		GetBLOBSize( const bool GetAnyway = false ) const;

	/**
		\brief Return a BLOB pointer
	*/
	long int *	GetBLOB() const;

	bool CheckForDecimals();

///////////////////////////
// INTERNAL FUNCTIONS
///////////////////////////
public:
	// Used in printing 
	int charoutput( char* dest, int max_size, bool xml_convert = false ) const;
	void OutputTransport( XCTransport* transport, char* temp ) const;

	// Release is only to be used when Copy was used to create a field!!!!
	void Release();

	// Validate field data against field flags
	void Validate();

	XCBuffer* GetOwner() const { return owner; };
	int		CalcStringSize() const;

	double	DurationToDouble() const;
	void	DoubleToDuration( double value );
	buffer_ds* GetDSInformation() const { return mydesc; };

	// For xmlint, returns base 64 encoding of BLOB data
	void		GetBLOBbase64( char *buffer, int maxsize = 0) const;
	long		GetBLOBbase64Size() const;

	// For xmlint, sets BLOB data based on base64 encoded string
	void SetBLOBbase64( const long size, const char *base64);

	double	ToJulian() const;
	void	FromJulian( double jul_date );

	void	FromGregorian( date_conv_type& date );

protected:
	// Create a dynamic field
	void CreateDynamic( XCAppInterface* my_appdic, char* new_buffer, long new_length, int new_dec_places, int new_type, int new_min_len, int new_flags );

	//
	// Internal movement routines
	//
	// Load structure data (returns field number)
	int LoadStructureData( buffer_ds* field_info, XCBuffer* newowner );

	bool	conversion_tbl_lookup( long stype );
	void	string_or_numeric_move( const XCField* source, bool MoveBlanks );
	void	BLOB_move( const XCField* source, bool MoveBlanks );

	void	date_move( const XCField* source, bool MoveBlanks );
	void	AdjustDate( date_conv_type& date );

	void	time_move( const XCField* source, bool MoveBlanks );

	void 	duration_move( const XCField* source, bool MoveBlanks );

	//
	// Data validation routines
	//
	bool	Valid_String() const;
	bool	Valid_Alpha_Str() const;
	bool	Valid_BLOB() const;
	bool	Chk_Display() const;
	bool	Chk_Zoned() const;
	bool	Chk_Time( char* buffer, long size, long type ) const;
	bool	Chk_Date( char* buffer ) const;
	bool	Chk_Duration( int type ) const;

	//
	// Internal variables
	//

    long				number;			// Our field number
    int					type;			// Field type
    long				size;			// Defined field size
	long				allocsize;		// Actual alloted size
    long				dec_places;		// Number of decimal places
    int					min_len;		// Our minimum length
	short int			flags;			// Field flags
	bool				user_entered;	// True if user has data has been overridden with non-default value

    char*				buffer_offset;	// Pointer to actual data in the buffer
    buffer_ds			*mydesc;		// Pointer to our field info (name, etc)
	XCBuffer*			owner;			// Pointer to buffer that owns us

	// Unknown at this time
	XCAppInterface*	appdic;
    bool tempbuffer;
	bool validblob;
	int *count;

	// Not used
	int LeftJustify();

	// Not used
	int RightJustify();

	bool AllowBlankLiterals() { return flags && 512; };

	// Returns true if the field is explicitly blank
	bool IsExplicitBlank() const;

private:
	void HR24_TO_TIME_TARGET( char* HR24, XCField* target );
	void TIME_SOURCE_TO_24HR( const XCField* source, char* HR24 );
	void HR24_TO_DISPLAY( char* HR24, char* DISPLAY );
	void DISPLAY_TO_24HR( char* DISPLAY, char* HR24);
	void DISPLAY_TO_NBR_TARGET( XCAppInterface* appdic, const XCField* source, XCField* target, char* DISPLAY );
	void NBR_SOURCE_TO_DISPLAY( XCAppInterface* appdic, const XCField* source, XCField* target, char* DISPLAY );
	void HH_TO_DUR_TARGET( XCField* target, char* HH, int HH_LENGTH, int HH_DEC_PLACES );
	void DUR_SOURCE_TO_HH( XCField* source, char* HH, int HH_LENGTH, int HH_DEC_PLACES );
	void HM_TO_HH_DISP( char* buffer, int length, char* HH, int HH_DEC_PLACES );
	void HH_DISP_TO_HM( char* buffer, int length, char* HH );

	buffer_field* field_descr;

	bool changed;
	bool truncated;
	bool read_only;
	long desc_message;
};

#endif

